import java.io.File;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Scanner;


public class NFA 
{
	private static final String STATES = "STATES";
	private static final String START = "START";
	private static final String ACCEPTING = "ACCEPT";
	
	private ArrayList<State> states = new ArrayList<State>();
	private State start;
	private List<State> accepting;
	private Map<State, List<Transition>> transitions;
	
	public NFA(File nfaFile)
	{
		Scanner scanner = null;
		try 
		{
			scanner = new Scanner(nfaFile);
		} 
		catch (FileNotFoundException e)
		{
			e.printStackTrace();
		}
		
		while (scanner.hasNextLine()) 
		{
			Scanner s2 = new Scanner(scanner.nextLine());
		    boolean b;
		    while (b = s2.hasNext()) 
		    {	
		    	String line = s2.nextLine();

				if (line.isEmpty()) {
					continue;
				}

				if (line.indexOf(STATES) == 0) {
					try {
						processStates(line);
					} catch (FileFormatException e) {
						e.printStackTrace();
					}
				} else if (line.indexOf(START) == 0) {
					try {
						processStart(line);
					} catch (FileFormatException e) {
						e.printStackTrace();
					}
				} else if (line.indexOf(ACCEPTING) == 0) {
					try {
						processAccepting(line);
					} catch (FileFormatException e) {
						e.printStackTrace();
					}
				} else {
					processTransition(line);
				}
		        
		    }
		}
		scanner.close();
	}
	
	//do not use
	public HashMap<State, List<Transition>> getTable()
	{
		HashMap<State, List<Transition>> myMap = new HashMap<State, List<Transition>>();
		for(int i =0; i < states.size(); i++)
		{
			myMap.put(states.get(i), states.get(i).getTransitions());
		}
		return myMap;
	}
	
	public ArrayList<State> getStates()
	{
		return states;
	}
	
	public State getStartState()
	{
		return start;
	}
	
	/*
	 *  STATES
	 */
	private final void processStates(String line) throws FileFormatException {
		String[] parts = postSpace(line).split(",");
		
		for (int i = 0; i < parts.length; i++)
		{
			//System.out.println(parts[i]);
			states.add(new State(parts[i]));
		}
	}

	/*
	 *  START
	 */
	private final void processStart(String line) throws FileFormatException {
		String label = postSpace(line);
		Boolean gotStart = false;
		if(!states.isEmpty())
		{
			for(int i = 0; i < states.size(); i++)
			{
				if (states.get(i).getName().equals(label))
				{
					start = states.get(i);
					gotStart = true;
				}
			}
			
			if(gotStart == false)
			{
				System.out.println("Not found the start state; making own");
				State myStart = new State(label);
				states.add(myStart);
				start = myStart;
			}
		}
	}

	/*
	 *  ACCEPTING
	 */
	private final void processAccepting(String line) throws FileFormatException {
		String[] parts = line.split(" ");
		String tokenClass = parts[1];
		String[] stateLabels = parts[2].split(",");

		for (int i = 0; i < stateLabels.length; i++)
		{
			for (int j = 0; j < states.size(); j++)
			{
				if (states.get(j).getName().equals(stateLabels[i]))
				{
					states.get(j).setAceept(true);
					states.get(j).setToken(tokenClass);
				}
			}
		}
	}

	private final void processTransition(String line) 
	{
		String from,pattern,to;
		String[] parts = line.split(",");
		
		if(parts.length == 3)
		{
			from = parts[0];
			pattern = parts[1];
			to = parts[2];
		}
		else
		{
			from = parts[0];
			pattern = "All";
			to = parts[1];
		}

		Matcher matcher = new Matcher(pattern);
		for (int i =0; i < states.size(); i++)
		{
			if (states.get(i).getName().equals(from))
			{
				if(states.get(i).getTransitions() == null )
				{
					ArrayList<Transition> thisTrans = new ArrayList<Transition>();  //empty so make my own
					ArrayList<State> nextStates = new ArrayList<State>();
					
					for(int j = 0; j < states.size(); j++)
					{
						if (states.get(j).getName().equals(to))
						{
							nextStates.add(states.get(j));
						}
					}
					
					Transition myTran = new Transition(matcher, nextStates);
					thisTrans.add(myTran);
					states.get(i).setTransitions(thisTrans);
					
				}
				else
				{
					ArrayList<Transition> thisTrans = states.get(i).getTransitions(); //already exist so get it and then append
					Boolean match = false;
					for (int j =0; j < thisTrans.size(); j++)
					{
						if(thisTrans.get(j).getInput().equals(pattern))  //matched input
						{
							ArrayList<State> getStates = thisTrans.get(j).getStates();
							
							for(int l = 0; l < states.size(); l++)
							{
								if (states.get(l).getName().equals(to))
								{
									if(!getStates.contains(states.get(l)))
									{
										getStates.add(states.get(l)); //doesnt contain already
									}
								}
							}
							
							thisTrans.get(j).setStates(getStates);
							match = true;
						}
					}
					
					if(match == false)  //no existing transition exists for the pattern
					{
						Boolean makeNew = true;
						//need to make a new transition and put it in
						for(int l = 0; l < states.size(); l++)
						{
							if (states.get(l).getName().equals(to))
							{
								ArrayList<State> put = new ArrayList<State>();
								put.add(states.get(l));
								thisTrans.add(new Transition(matcher, put));
								makeNew = false;
							}
						}
						
						if(makeNew == true)
						{
							ArrayList<State> put = new ArrayList<State>();
							put.add(new State(to));
							thisTrans.add(new Transition(matcher, put));
							makeNew = false;
						}
					}
					
					states.get(i).setTransitions(thisTrans); //put the updated arraylist of transitions
				}
			}
		}
	}
	
	private String postSpace(String line) throws FileFormatException {
		int space = line.indexOf(' ');

		if (space == -1) {
			throw new FileFormatException();
		}

		String postSpace = line.substring(space + 1);
		return postSpace;
	}
	
	public static class FileFormatException extends Exception {
		private static final long serialVersionUID = 8448704966112475242L;

		public FileFormatException() {
		}

		public FileFormatException(String message, Throwable cause,
				boolean enableSuppression, boolean writableStackTrace) {
			super(message, cause, enableSuppression, writableStackTrace);
		}

		public FileFormatException(String message, Throwable cause) {
			super(message, cause);
		}

		public FileFormatException(String message) {
			super(message);
		}

		public FileFormatException(Throwable cause) {
			super(cause);
		}
	}
}
